﻿namespace TheoryProject.Task1
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// Direction Enum
    /// </summary>
    public enum Direction
    {
        /// <summary>
        /// left direction 
        /// </summary>
        LEFT,

        /// <summary>
        /// right direction
        /// </summary>
        RIGHT,

        /// <summary>
        /// stay (no direction)
        /// </summary>
        STAY
    }

    /// <summary>
    /// transition structure
    /// </summary>
    public struct Transition
    {
        /// <summary>
        /// Gets or sets the state.
        /// </summary>
        /// <value>
        /// The state.
        /// </value>
        public int State
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the next state.
        /// </summary>
        /// <value>
        /// The state of the next.
        /// </value>
        public int NextState
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the input char.
        /// </summary>
        /// <value>
        /// The input char.
        /// </value>
        public char InputChar
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the direction.
        /// </summary>
        /// <value>
        /// The direction.
        /// </value>
        public Direction Direction
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the output char.
        /// </summary>
        /// <value>
        /// The output char.
        /// </value>
        public char OutputChar
        {
            get;
            set;
        }
    }

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class TuringMachine
    {
        /// <summary>
        /// array of transitions
        /// </summary>
        private Transition[] transitions;

       /// <summary>
        /// the head    
       /// </summary>
        private int head;
        
        /// <summary>
        /// current state
        /// </summary>
        private int state;

        /// <summary>
        /// array of final states
        /// </summary>
        private int[] finalStates;

        /// <summary>
        /// Initializes a new instance of the <see cref="TuringMachine"/> class.
        /// </summary>
        /// <param name="transitions">The transitions.</param>
        /// <param name="tape">The tape.</param>
        /// <param name="finalS">The final S.</param>
        public TuringMachine(Transition[] transitions, List<char> tape, int[] finalS)
        {
            try
            {
                this.transitions = transitions;
                this.Tape = tape;
                this.head = 0;
                this.state = 0;
                this.finalStates = finalS;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Gets or sets the tape.
        /// </summary>
        /// <value>
        /// The tape.
        /// </value>
        public List<char> Tape
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the transition.
        /// </summary>
        /// <param name="inputChar">The input char.</param>
        /// <param name="state">The state.</param>
        /// <returns>next transition</returns>
        public Transition? GetTransition(char inputChar, int state)
        {
            try
            {
                for (int i = 0; i < this.transitions.Length; i++)
                {
                    if ((this.transitions[i].State == state) && (this.transitions[i].InputChar == inputChar))
                    {
                        return this.transitions[i];
                    }
                }

                return null;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Processes this instance.
        /// </summary>
        /// <returns>the proceeding output (accept --> true) (otherwise --> false)</returns>
        public bool Process()
        {
            try
            {
                Transition? nextTransition;
                while (true)
                {
                    nextTransition = this.GetTransition(this.Tape[this.head], this.state);

                    if (nextTransition != null)
                    {
                        Transition temp = (Transition)nextTransition;

                        this.state = temp.NextState;
                        this.Tape[this.head] = temp.OutputChar;
                        if (temp.Direction == Direction.LEFT)
                        {
                            this.head--;
                            if (this.head < 0)
                            {
                                this.Tape.Insert(0, '\0');
                                this.head = 0;
                            }
                        }
                        else if (temp.Direction == Direction.RIGHT)
                        {
                            this.head++;
                            if (this.head == this.Tape.Count)
                            {
                                this.Tape.Insert(this.Tape.Count, '\0');
                                this.head = this.Tape.Count - 1;
                            }
                        }

                        if (this.IsFinalState(temp.NextState))
                        {
                            return true;
                        }
                    }
                    else
                    {
                        return false;
                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Determines whether [is final state] [the specified state].
        /// </summary>
        /// <param name="state">The state.</param>
        /// <returns>
        ///   <c>true</c> if [is final state] [the specified state]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsFinalState(int state)
        {
            try
            {
                int length = this.finalStates.Length;
                for (int i = 0; i < length; i++)
                {
                    if (state == this.finalStates[i])
                    {
                        return true;
                    }
                }

                return false;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}